
#include <nxos_mini.h>

// #ifdef CONFIG_NX_DRIVER_AHCI

#include <pci.h>
#include <io.h>
#include <dma_region.h>
#include <base/mutex.h>
#include <base/barrier.h>
#include <base/memory.h>
#include <base/thread.h>
#include <base/debug.h>
#include <base/driver.h>
#include <base/ioremap.h>
#include <base/irq.h>
#include <base/string.h>
#include <base/uaccess.h>
#include <base/page.h>
#define NX_LOG_NAME "PCNET32"
#define NX_LOG_LEVEL NX_LOG_INFO
#include <base/log.h>
#include <base/malloc.h>
#include <base/msgqueue.h>
#include <drvfw/netif.h>

#define DRV_VERSION "v0.1"
#define DRV_NAME "net-pcnet32" DRV_VERSION

#define DEV_NAME "pcnet32"

// #define DEBUG_DRV

#define DEBUG_ARP 0

#define DEBUG_INITBLOCK 0

#define ETH_ALEN 6  /* MAC addr */
#define ETH_ZLEN 60 /* Minimum length of data without CRC check */
#define ETH_DATA_LEN 1500 /* Maximum length of data in a frame */
#define ETH_FRAME_LEN 1518 /* Maximum Ethernet data length */

#define RX_MSG_CNT 8  /* 4 msg queue */
#define RX_MSG_SIZE (ETH_FRAME_LEN + 4)  /* 4 save real msg size */

#define TX_CACHE_BUF_SIZE          (2048)

#define PCNET32_VENDOR_ID   0x1022
#define PCNET32_DEVICE_ID   0x2000

/* Offsets from base I/O address. */
#define PCNET32_WIO_RDP     0x10
#define PCNET32_WIO_RAP     0x12
#define PCNET32_WIO_RESET   0x14
#define PCNET32_WIO_BDP     0x16

#define CSR0        0
#define CSR0_INIT   0x1
#define CSR0_START  0x2
#define CSR0_STOP   0x4
#define CSR0_TXPOLL 0x8
#define CSR0_INTEN  0x40
#define CSR0_IDON   0x0100
#define CSR0_NORMAL (CSR0_START | CSR0_INTEN)
#define CSR0_TINT   0x0200  /* Transmit Interrupt */
#define CSR0_RINT   0x0400  /* Receive Interrupt */
#define CSR0_MERR   0x0800  /* Memory Error */
#define CSR0_MISS   0x1000  /* Missed Frame */
#define CSR0_CERR   0x2000  /* Collision Error */
#define CSR0_BABL   0x4000  /* Babble is a transmitter time-out error. */

/* Error is set by the ORing of BABL, CERR, MISS, and MERR.
 * ERR remains set as long as any of the error flags are true.
 */
#define CSR0_ERR   0x8000

#define CSR1        1
#define CSR2        2
#define CSR3        3   /*  Interrupt Masks and Deferral Control */
#define CSR3_IDONM  (1 << 8)   /* Initialization Done Mask. */
#define CSR4        4   /* Test and Features Control */
#define CSR4_ASTRP_RCV  (1 << 10)   /* Auto Strip Receive */
#define CSR4_APAD_XMT   (1 << 11)   /* Auto Pad Transmit */

#define CSR5        5
#define CSR5_SUSPEND    0x0001
#define CSR6        6   /* RX/TX Descriptor Table Length */

#define CSR15       15  /* Mode */
#define CSR18       18  /* Current Receive Buffer Address Lower */
#define CSR19       19  /* Current Receive Buffer Address Upper */
#define CSR24       24  /* Base Address of Receive Descriptor Ring Lower */
#define CSR25       25  /* Base Address of Receive Descriptor Ring Upper */
#define CSR30       30  /* Base Address of Transmit Descriptor Ring Lower */
#define CSR31       31  /* Base Address of Transmit Descriptor Ring Upper */

#define CSR58       58  /* Software Style */
#define CSR58_PCNET_PCI_II   0x02

#define PCNET32_INIT_LOW    1
#define PCNET32_INIT_HIGH   2
#define PCNET32_MC_FILTER 8   /* broadcast filter */

#define CSR72       72  /* Receive Descriptor Ring Counter */
#define CSR74       74  /* Transmit Descriptor Ring Counter */

#define BCR2        2
#define BCR2_ASEL   (1 << 1)

#define PCNET32_TX_BUFFERS 8
#define PCNET32_RX_BUFFERS 32
#define PCNET32_LOG_TX_BUFFERS 3    /* 2^3 = 8 buffers */
#define PCNET32_LOG_RX_BUFFERS 5    /* 2^5 = 32 buffers */

#define PCNET32_RING_DE_SIZE     16

#define PCNET32_TX_RETRY     10 /* tx retry counter when no available descriptor entry */

#define PCNET32_DESC_STATUS_OWN 0x8000  /* card own the desc */

/**
 * End of Packet indicates that this is the last buffer used by
 * the PCnet-PCI II controller for this frame.
 */
#define PCNET32_DESC_STATUS_ENP 0x0100

/**
 * Start of Packet indicates that this
 * is the first buffer used by the
 * PCnet-PCI II controller for this
 * frame.
 */
#define PCNET32_DESC_STATUS_STP 0x0200

#define GET_PCNET32(eth)    (DeviceExtension *)(eth)

/**
 * rx ring desc struct
 */
struct pcnet32_rx_desc
{
    NX_U32 base;   /* buffer base addr */
    NX_U16 buf_length; /* two`s complement of length */
    NX_U16 status; /* desc status */
    NX_U16 msg_length; /*  Message Byte Count is the length in bytes of the received message. */
    NX_U16 rpc_rcc;
    NX_U32 reserved;
} __attribute__ ((packed));

/**
 * tx ring desc struct
 */
struct pcnet32_tx_desc
{
    NX_U32 base;   /* buffer base addr */
    NX_U16 buf_length; /* two`s complement of length */
    NX_U16 status; /* desc status */
    NX_U32 misc;
    NX_U32 reserved;
} __attribute__ ((packed));

/**
 * The PCNET32 32-Bit initialization block, described in databook.
 * The Mode Register (CSR15) allows alteration of the chip's operating
 * parameters. The Mode field of the Initialization Block is copied directly
 * into CSR15. Normal operation is the result of configuring the Mode field
 * with all bits zero.
 */
struct pcnet32_init_block
{
    NX_U16 mode;
    NX_U16 tlen_rlen;
    NX_U8  phys_addr[6];
    NX_U16 reserved;
    NX_U32 filter[2];
    /* Receive and transmit ring base, along with extra bits. */
    NX_U32 rx_ring;
    NX_U32 tx_ring;
} __attribute__ ((packed));

typedef struct DeviceExtension {
    /* interface address info. */
    NX_U8 dev_addr[ETH_ALEN];         /* MAC address  */

    NX_PciDevice *pci_dev;   /* pci device info */
    NX_U32 flags;
    NX_U32 iobase; /* io port base */
    NX_U32 irqno;  /* irq number */

    struct pcnet32_init_block *init_block;

    NX_U16 rx_len_bits;
    NX_U16 tx_len_bits;

    NX_U32 rx_ring_dma_addr;
    NX_U32 tx_ring_dma_addr;

    NX_U32 init_block_dma_addr;

    NX_U32 rx_buffer_ptr;
    NX_U32 tx_buffer_ptr;  /* pointers to transmit/receive buffers */

    NX_Size rx_buffer_count;   /* total number of receive buffers */
    NX_Size tx_buffer_count;   /* total number of transmit buffers */

    NX_Size buffer_size;  /* length of each packet buffer */

    NX_Size de_size;    /* length of descriptor entry */

    struct pcnet32_rx_desc *rdes;   /* pointer to ring buffer of receive des */
    struct pcnet32_tx_desc *tdes;   /* pointer to ring buffer of transmit des */

    NX_U32 rx_buffers; /* physical address of actual receive buffers (< 4 GiB) */
    NX_U32 tx_buffers; /* physical address of actual transmit buffers (< 4 GiB) */

    NX_MsgQueue *rx_queue;    /* 接收队列 */
} DeviceExtension;

static DeviceExtension PCNET32_Device;

/**
 * does the driver own the particular buffer?
 */
static inline NX_Bool pcnet32_is_driver_own(DeviceExtension *dev, NX_Bool is_tx, NX_U32 idx)
{
    return (NX_Bool)(is_tx ? ((dev->tdes[idx].status & PCNET32_DESC_STATUS_OWN) == 0) :
                   ((dev->rdes[idx].status & PCNET32_DESC_STATUS_OWN) == 0));
}

/*
 * get the next desc buffer index
 */
static inline NX_U32 pcnet32_get_next_desc(DeviceExtension *dev, NX_U32 cur_idx, NX_U32 buf_count)
{
    return (cur_idx + 1) % buf_count;
}

static void pcnet32_init_rx_desc_entry(DeviceExtension *dev, NX_U32 idx)
{
    struct pcnet32_rx_desc *des = dev->rdes + idx;
    NX_MemSet(des, 0, dev->de_size);

    des->base = NX_Virt2Phy(dev->rx_buffers + idx * dev->buffer_size);

    /* next 2 bytes are 0xf000 OR'd with the first 12 bits of the 2s complement of the length */
    NX_U16 bcnt = (NX_U16)(-dev->buffer_size);
    bcnt &= 0x0fff;
    bcnt |= 0xf000; /* high 4 bits fixed 1 */
    des->buf_length = bcnt;

    /* finally, set ownership bit - transmit buffers are owned by us, receive buffers by the card */
    des->status = PCNET32_DESC_STATUS_OWN;
}

static void pcnet32_init_tx_desc_entry(DeviceExtension *dev, NX_U32 idx)
{
    struct pcnet32_tx_desc *des = dev->tdes + idx;
    NX_MemSet(des, 0, dev->de_size);

    des->base = NX_Virt2Phy(dev->tx_buffers + idx * dev->buffer_size);

    /* next 2 bytes are 0xf000 OR'd with the first 12 bits of the 2s complement of the length */
    NX_U16 bcnt = (NX_U16)(-dev->buffer_size);
    bcnt &= 0x0fff;
    bcnt |= 0xf000; /* high 4 bits fixed 1 */
    des->buf_length = bcnt;
}

static NX_U16 pcnet32_wio_read_mac(NX_U32 addr, int index)
{
    return IO_In16(addr + index);
}

/*
 * write index to RAP, read data from RDP
 */
static NX_U16 pcnet32_wio_read_csr(NX_U32 addr, int index)
{
    IO_Out16(addr + PCNET32_WIO_RAP, index);
    return IO_In16(addr + PCNET32_WIO_RDP);
}

/**
 * write index to RAP, write data to RDP
 */
static void pcnet32_wio_write_csr(NX_U32 addr, int index, NX_U16 val)
{
    IO_Out16(addr + PCNET32_WIO_RAP, index);
    IO_Out16(addr + PCNET32_WIO_RDP, val);
}

static void pcnet32_wio_write_bcr(NX_U32 addr, int index, NX_U16 val)
{
    IO_Out16(addr + PCNET32_WIO_RAP, index);
    IO_Out16(addr + PCNET32_WIO_BDP, val);
}

/*
 * Reset causes the device to cease operation and clear its internal logic.
 */
static void pcnet32_wio_reset(NX_U32 addr)
{
    IO_In16(addr + PCNET32_WIO_RESET);
}

static int pcnet32_get_pci(DeviceExtension *dev)
{
    /* get pci device */
    NX_PciDevice *pci_dev = NX_PciDeviceGet(PCNET32_VENDOR_ID, PCNET32_DEVICE_ID);
    if (pci_dev == NX_NULL)
    {
        NX_LOG_E("pcnet32: device not find on pci device.");
        return -1;
    }
    dev->pci_dev = pci_dev;
    NX_LOG_I("pcnet32: find device, vendor id: 0x%x, device id: 0x%x",
          pci_dev->vendor_id, pci_dev->device_id);

    /* enable bus mastering */
    NX_PciEnableBusMastering(pci_dev);

    /* get io port address */
    dev->iobase = NX_PciDeviceGetIoAddr(pci_dev);
    if (dev->iobase == 0)
    {
        NX_LOG_E("pcnet32: invalid pci device io address.");
        return -1;
    }
    NX_LOG_I("pcnet32: io base address: 0x%x", dev->iobase);
    /* get irq */
    dev->irqno = NX_PciDeviceGetIrqLine(pci_dev);
    if (dev->irqno == 0xff)
    {
        NX_LOG_E("pcnet32: invalid irqno.");
        return -1;
    }
    NX_LOG_I("pcnet32: irqno %d", dev->irqno);
    return 0;
}

static int pcnet32_transmit(DeviceExtension *dev, NX_U8 *buf, NX_Size len)
{
    if(len > ETH_FRAME_LEN)
    {
        len = ETH_FRAME_LEN;
    }

    NX_U32 tx_retry = PCNET32_TX_RETRY;

    while (tx_retry > 0)
    {
        /* the next available descriptor entry index is in tx_buffer_ptr */
        if(!pcnet32_is_driver_own(dev, NX_True, dev->tx_buffer_ptr))
        {
            /* try encourage the card to send all buffers. */
            pcnet32_wio_write_csr(dev->iobase, CSR0, pcnet32_wio_read_csr(dev->iobase, CSR0) | CSR0_TXPOLL);
        }
        else
        {
            break;
        }
        --tx_retry;
    }

    if (!tx_retry)  /* retry end, no entry available */
    {
        NX_LOG_E("transmit no available descriptor entry");
        return -1;
    }

    NX_MemCopy((void *)(dev->tx_buffers + dev->tx_buffer_ptr * dev->buffer_size), buf, len);

    struct pcnet32_tx_desc *tdes = dev->tdes + dev->tx_buffer_ptr;
    /**
     * set the STP bit in the descriptor entry (signals this is the first
     * frame in a split packet - we only support single frames)
     */
    tdes->status |= PCNET32_DESC_STATUS_STP;

    /* similarly, set the ENP bit to state this is also the end of a packet */
    tdes->status |= PCNET32_DESC_STATUS_ENP;

    NX_U16 bcnt = (NX_U16)(-len);
    bcnt &= 0xfff;
    bcnt |= 0xf000; /* high 4 bits fixed 1 */
    tdes->buf_length = bcnt;

    /* finally, flip the ownership bit back to the card */
    tdes->status |= PCNET32_DESC_STATUS_OWN;

    dev->tx_buffer_ptr = pcnet32_get_next_desc(dev, dev->tx_buffer_ptr, dev->tx_buffer_count);
    return 0;
}

NX_PRIVATE NX_Error PCNET32_Read(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    char rxbuf[1600] = {0};
    NX_MsgBuf mbuf = {.payload = rxbuf, .len = 1600};
    DeviceExtension *dev = GET_PCNET32(device->extension);
    NX_Error err;
    
    /* wait on queue */
    err = NX_MsgQueueTryRecv(dev->rx_queue, &mbuf);
    if (err != NX_EOK)
    {
        return NX_EIO;
    }

    // NX_LOG_W("rx: bytes %d / %d\n", mbuf.len, len);
    mbuf.len = NX_MIN(mbuf.len, len);
    NX_CopyToUser((char *)buf, (char *)mbuf.payload, mbuf.len);

    if (mbuf.len != 0 && outLen)
    {
        NX_CopyToUser((char *)outLen, (char *)&mbuf.len, sizeof(NX_Size));
    }
    
    NX_LOG_D("DEV-RX: %d", mbuf.len);
    return NX_EOK;
}

NX_PRIVATE NX_Error PCNET32_Write(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    NX_Size txlen;
    NX_U8 txbuf[1600];
    NX_CopyFromUser((char *)txbuf, (char *)buf, len);

    if (pcnet32_transmit(device->extension, txbuf, len) < 0)
    {
        txlen = 0;
    }
    else
    {
        txlen = len;
    }
    if (outLen)
    {
        NX_CopyToUser((char *)outLen, (char *)&txlen, sizeof(NX_Size));
    }
    NX_LOG_D("DEV-TX: %d", txlen);
    return txlen > 0 ? NX_EOK : NX_EIO;
}

NX_PRIVATE NX_Error PCNET32_Control(struct NX_Device *device, NX_U32 cmd, void *arg)
{
    DeviceExtension *dev = GET_PCNET32(device->extension);
    switch (cmd)
    {
    case NX_NETIF_GET_MAC:
        NX_CopyToUser((char *)arg, (char *)&dev->dev_addr[0], ETH_ALEN);
        break;
    default:
        return NX_EINVAL;
    }
    return NX_EOK;
}

static void pcnet32_rx_packet(DeviceExtension *dev)
{
    NX_Error err;
    while (pcnet32_is_driver_own(dev, NX_False, dev->rx_buffer_ptr))
    {
        struct pcnet32_rx_desc *rdes = dev->rdes + dev->rx_buffer_ptr;
        NX_U32 plen = rdes->msg_length; /* msg len no need to negate it unlike BCNT above */

        void *pbuf = (void *)(dev->rx_buffers + dev->rx_buffer_ptr * dev->buffer_size);
        NX_LOG_D("ISR-RX: ring=%d, buf=%p, len=%d", dev->rx_buffer_ptr, pbuf, plen);
        /* merge size and data into receive pkg */
        // TODO: io_device_queue_append(&dev->rx_queue, pbuf, plen);
#if DEBUG_ARP == 0
        NX_MsgBuf buf = {.payload = pbuf, .len = plen};
        err = NX_MsgQueueTrySend(dev->rx_queue, &buf);
        if (err != NX_EOK)
        {
            NX_LOG_E("recv pbuf failed %d!\n", err);
        }
#else
        int *frame = (int *)pbuf;
        for (int i = 0; i < plen / 4 + 1; i++)
        {
            NX_Printf("%x ", frame[i]);
            if (i % 8 == 0)
            {
                NX_Printf("\n");	
            }
        }
        NX_LOG_D("\n-------------------\n");
#endif
        /* hand the buffer back to the card */
        rdes->status = PCNET32_DESC_STATUS_OWN;

        dev->rx_buffer_ptr = pcnet32_get_next_desc(dev, dev->rx_buffer_ptr, dev->rx_buffer_count);
    }
}

NX_PRIVATE NX_Error pcnet32_handler(NX_IRQ_Number vector, void *param)
{
    DeviceExtension *dev = GET_PCNET32(param);
    NX_Error intrhandled = NX_EOK;

    NX_U32 iobase = dev->iobase;
    NX_U32 csr0 = pcnet32_wio_read_csr(iobase, 0);

    if (csr0 & CSR0_RINT) /* recv packet */
    {
        NX_LOG_D("ISR-RX: occur");
        pcnet32_rx_packet(dev);
    }
    else if ((csr0 & CSR0_TINT))    /* packet transmitted */
    {
        NX_LOG_D("ISR-TX: occur");
    }
    else if ((csr0 & CSR0_IDON))
    {
        NX_LOG_I("ISR-INIT: occur(nic init done)");
    }
    else if ((csr0 & CSR0_MERR))
    {
        NX_LOG_W("memory error!");
    }
    else if ((csr0 & CSR0_MISS))
    {
        NX_LOG_W("missed frame!");
    }
    else if ((csr0 & CSR0_CERR))
    {
        NX_LOG_W("collision error!");
    }
    else if ((csr0 & CSR0_BABL))
    {
        NX_LOG_W("transmitter time-out error!");
    }
    else
    {
        intrhandled = NX_ENORES;
        NX_LOG_W("unknown intr");
    }
    /* ack pcnet32 interrupt as handled */
    pcnet32_wio_write_csr(iobase, 0, csr0);
    return intrhandled;
}

static int pcnet32_alloc_ring_buffer(DeviceExtension *dev)
{

    dev->rdes = NX_MemAllocAlign(dev->rx_buffer_count * dev->de_size, 16);
    // NX_DmaRegion dma;
    // dma.size = dev->rx_buffer_count * dev->de_size;
    // dma.alignment = 0x1000;
    // if (NX_DmaAllocBuffer(&dma) != NX_EOK)
    // {
    //     NX_LOG_E("alloc dma buff failed!");
    //     return -1;
    // }
    // dev->rdes = dma.vaddr;
    if (dev->rdes == NX_NULL)
    {
        NX_LOG_E("alloc memory for rx ring failed!");
        return -1;
    }
    dev->tdes = NX_MemAllocAlign(dev->tx_buffer_count * dev->de_size, 16);
    // dma.size = dev->tx_buffer_count * dev->de_size;
    // dma.alignment = 0x1000;
    // if (NX_DmaAllocBuffer(&dma) != NX_EOK)
    // {
    //     NX_LOG_E("alloc dma buff failed!");
    //     return -1;
    // }
    // dev->tdes = dma.vaddr;
    if (dev->tdes == NX_NULL)
    {
        NX_LOG_E("alloc memory for tx ring failed!");
        NX_MemFree(dev->rdes);
        return -1;
    }

    dev->rx_buffers = (NX_U32)NX_MemAllocAlign(dev->rx_buffer_count * dev->buffer_size, 16);
    // dma.size = dev->rx_buffer_count * dev->buffer_size;
    // dma.alignment = 0x1000;
    // if (NX_DmaAllocBuffer(&dma) != NX_EOK)
    // {
    //     NX_LOG_E("alloc dma buff failed!");
    //     return -1;
    // }
    // dev->rx_buffers = dma.vaddr;
    if (dev->rx_buffers == 0)
    {
        NX_LOG_E("alloc memory for rx ring buffer failed!");
        NX_MemFree(dev->rdes);
        NX_MemFree(dev->tdes);
        return -1;
    }

    dev->tx_buffers = (NX_U32)NX_MemAllocAlign(dev->tx_buffer_count * dev->buffer_size, 16);
    // dma.size = dev->tx_buffer_count * dev->buffer_size;
    // dma.alignment = 0x1000;
    // if (NX_DmaAllocBuffer(&dma) != NX_EOK)
    // {
    //     NX_LOG_E("alloc dma buff failed!");
    //     return -1;
    // }
    // dev->tx_buffers = dma.vaddr;
    if (dev->tx_buffers == 0)
    {
        NX_LOG_E("alloc memory for tx ring buffer failed!");
        NX_MemFree(dev->rdes);
        NX_MemFree(dev->tdes);
        NX_MemFree((void *)dev->rx_buffers);
        return -1;
    }
    NX_LOG_D("rdes:%p tdes:%p rbuf:%p tbuf:%p", dev->rx_buffer_count * dev->de_size,
        dev->tx_buffer_count * dev->de_size,
        dev->rx_buffer_count * dev->buffer_size,
        dev->tx_buffer_count * dev->buffer_size);
    NX_LOG_D("rdes:%p tdes:%p rbuf:%p tbuf:%p", dev->rdes, dev->tdes, dev->rx_buffers, dev->tx_buffers);

    int i = 0;
    for (i = 0; i < dev->rx_buffer_count; i++)
    {
        pcnet32_init_rx_desc_entry(dev, i);
    }
    for (i = 0; i < dev->tx_buffer_count; i++)
    {
        pcnet32_init_tx_desc_entry(dev, i);
    }
    return 0;
}

static void pcnet32_free_ring_buffer(DeviceExtension *dev)
{
    NX_MemFree(dev->rdes);
    NX_MemFree(dev->tdes);
    NX_MemFree((void *)dev->rx_buffers);
    NX_MemFree((void *)dev->tx_buffers);
}

#if DEBUG_INITBLOCK == 1
static void pcnet32_print_init_block(DeviceExtension *dev)
{
    NX_U32 iobase = dev->iobase;

    struct pcnet32_init_block *init_block = dev->init_block;
    NX_LOG_D("pcnet32 init block info:");
    NX_LOG_D("mode: %x, tlen_rlen:%x", init_block->mode, init_block->tlen_rlen);
    NX_LOG_D("mac: %x:%x:%x:%x:%x:%x",
            init_block->phys_addr[0],
            init_block->phys_addr[1],
            init_block->phys_addr[2],
            init_block->phys_addr[3],
            init_block->phys_addr[4],
            init_block->phys_addr[5]);
    NX_LOG_D("filter0: %x, filter1: %x", init_block->filter[0], init_block->filter[1]);
    NX_LOG_D("rx ring dma: %x, tx ring dma: %x", init_block->rx_ring, init_block->tx_ring);
    NX_LOG_D("init block dma: %x", dev->init_block_dma_addr);

    int i = 0;
    for (; i <= 46; i++)
    {
        NX_LOG_D("csr%d=%x", i, pcnet32_wio_read_csr(iobase, i));
    }
}
#endif

static int pcnet32_init(DeviceExtension *dev)
{
    NX_U32 iobase = dev->iobase;

    dev->flags = 0;

    /* init buffer info */
    dev->rx_buffer_ptr = 0;
    dev->tx_buffer_ptr = 0;

    dev->rx_buffer_count = PCNET32_RX_BUFFERS;
    dev->tx_buffer_count = PCNET32_TX_BUFFERS;

    dev->buffer_size = ETH_FRAME_LEN;
    dev->de_size = PCNET32_RING_DE_SIZE;

    if (pcnet32_alloc_ring_buffer(dev) != 0)
    {
        return -1;
    }

    dev->rx_ring_dma_addr = (NX_U32)NX_Virt2Phy(dev->rdes);
    dev->tx_ring_dma_addr = (NX_U32)NX_Virt2Phy(dev->tdes);

    /* 初始化接收队列，用内核队列结构保存，等待被读取 */
    dev->rx_queue = NX_MsgQueueCreate(1600, 64);
    if (dev->rx_queue == NX_NULL)
    {
        pcnet32_free_ring_buffer(dev);
        return -1;
    }
    /* alloc init block, must 16 bit align */
    // NX_DmaRegion dma;
    // dma.size = 0x1000;
    // dma.alignment = 0x1000;
    // if (NX_DmaAllocBuffer(&dma) != NX_EOK)
    // {
    //     NX_LOG_E("alloc dma buff failed!");
    //     return -1;
    // }

    dev->init_block = NX_MemAllocAlign(sizeof(struct pcnet32_init_block), 16);
    // dev->init_block = dma.vaddr;
    if (dev->init_block == NX_NULL)
    {
        NX_LOG_E("alloc memory for init block failed!");

        pcnet32_free_ring_buffer(dev);
        return -1;
    }
    dev->init_block_dma_addr = (NX_U32)NX_Virt2Phy(dev->init_block);
    // dev->init_block_dma_addr = dma.paddr;

    NX_LOG_D("init block addr:%p size:%d", dev->init_block, sizeof(struct pcnet32_init_block));

    /* fill init block */
    dev->init_block->mode = 0;
    dev->tx_len_bits = (PCNET32_LOG_TX_BUFFERS << 4);
    dev->rx_len_bits = (PCNET32_LOG_RX_BUFFERS << 4);
    dev->init_block->tlen_rlen = (dev->tx_len_bits << 8) | dev->rx_len_bits;

    int i = 0;
    for (i = 0; i < ETH_ALEN; i++)
    {
        dev->init_block->phys_addr[i] = dev->dev_addr[i];
    }
    dev->init_block->filter[0] = 0x00000000;
    dev->init_block->filter[1] = 0x00000000;
    dev->init_block->rx_ring = dev->rx_ring_dma_addr;
    dev->init_block->tx_ring = dev->tx_ring_dma_addr;

    NX_LOG_D("init addr:%p -> %p", dev->init_block, dev->init_block_dma_addr);

    /* register init block, CSR1 save low 16 bit, CSR1 save high 16 bit */
    pcnet32_wio_write_csr(iobase, CSR1, (dev->init_block_dma_addr & 0xffff));
    pcnet32_wio_write_csr(iobase, CSR2, (dev->init_block_dma_addr >> 16) & 0xffff);

    /* register intr */
    if (NX_IRQ_Bind(dev->irqno, pcnet32_handler, (void *)dev, DEV_NAME, NX_IRQ_FLAG_SHARED) != NX_EOK)
    {
        NX_LOG_E("install IRQ failed!");
        NX_MemFree(dev->init_block);

        pcnet32_free_ring_buffer(dev);
        return -1;
    }
    
    NX_IRQ_Unmask(dev->irqno);

    /* Start init */
    pcnet32_wio_write_csr(iobase, CSR0, CSR0_INIT | CSR0_INTEN);
    NX_LOG_D("card init done.");

    /* add auto pad amd strip recv */
    NX_U16 csr4 = pcnet32_wio_read_csr(iobase, CSR4);
    pcnet32_wio_write_csr(iobase, CSR4, csr4 | CSR4_ASTRP_RCV | CSR4_APAD_XMT);

    /* start work */
    pcnet32_wio_write_csr(iobase, CSR0, CSR0_START | CSR0_INTEN);

#if DEBUG_INITBLOCK == 1
    pcnet32_print_init_block(dev);
#endif

    return 0;
}

static int pcnet32_init_hw(DeviceExtension *dev)
{
    NX_U32 iobase = dev->iobase;

    /* reset card to 16 bit io mode */
    pcnet32_wio_reset(iobase);

    /* use dealy to wait reset done, at least 1 microsecond */
    // udelay(1000);
    NX_ClockTickDelayMillisecond(10);

    /* switch to 32 bit soft-style mode, use 32 bit struct */
    pcnet32_wio_write_bcr(iobase, 20, 0x102);

    /* stop card work */
    pcnet32_wio_write_csr(iobase, 0, 0x4);

    /* read mac addr */
    NX_U16 mac0 = pcnet32_wio_read_mac(iobase, 0);
    NX_U16 mac1 = pcnet32_wio_read_mac(iobase, 2);
    NX_U16 mac2 = pcnet32_wio_read_mac(iobase, 4);

    dev->dev_addr[0] = mac0 & 0xff;
    dev->dev_addr[1] = (mac0 >> 8) & 0xff;
    dev->dev_addr[2] = mac1 & 0xff;
    dev->dev_addr[3] = (mac1 >> 8) & 0xff;
    dev->dev_addr[4] = mac2 & 0xff;
    dev->dev_addr[5] = (mac2 >> 8) & 0xff;

    NX_LOG_D("MAC addr: %x:%x:%x:%x:%x:%x",
        dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
        dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);

    return pcnet32_init(dev);
}

NX_PRIVATE NX_Error ProbeDriver(NX_Driver *driver)
{
    NX_Device *device;
    NX_Error err;

    if (pcnet32_get_pci(&PCNET32_Device)) {

        return NX_ERROR;
    }

    if (pcnet32_init_hw(&PCNET32_Device)) {
        return NX_ERROR;
    }

    err = NX_DriverAttachDevice(driver, DEV_NAME, &device);
    if (err != NX_EOK)
    {
        return err;
    }
    device->extension = &PCNET32_Device;

    NX_LOG_I("probe success.");
    return NX_EOK;
}

NX_PRIVATE NX_DriverOps PCNET32_DriverOps = {
    .read       = PCNET32_Read,
    .write      = PCNET32_Write,
    .control    = PCNET32_Control,
};

#if DEBUG_ARP == 1
static void arp_test(void)
{
    unsigned char sendmsg[42] = 
    {
        /*前14位是以太网手部，后28位是ARP请求/应答
        MAC地址是是由48位组成的地址，为方便起见，通常用12位16进制表示，
        前6位16进制数字由IEEE负责统一分发，后6位16进制数字由各厂商自己负责管理。*/
       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,     //IEEE制定
       0x52, 0x54, 0x0, 0x12, 0x34, 0x56,     //这是我的MAC地址
       0x08, 0x06,                             //这是ARP的协议号，IEEE制定
       0x00, 0x01,                             //硬件类型0x0001表示以太网
       0x08, 0x00,                             //协议类型0x0800表示IP协议
       0x06, 0x04,                             //硬件地址（MAC地址）长度为6，协议地址（IP地址）长度为4
       0x00, 0x01,                             //opcode
       0x52, 0x54, 0x0, 0x12, 0x34, 0x56,     //发送端的MAC地址
       192,  168,  0,   200,                  //发送端的IP地址
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     //目的MAC地址,先置0，等待填写
       192,  168,  0,   100                   //目的IP地址
       };

    int i, j;

    for (i = 0; i < 50; i++)
    {
        pcnet32_transmit(&PCNET32_Device, sendmsg, 42);
        NX_ThreadSleep(5000);
    }
}
#endif

NX_PRIVATE void PCNET32_DriverInit(void)
{
    NX_Driver *driver = NX_DriverCreate(DRV_NAME, NX_DEVICE_TYPE_NET, 0, &PCNET32_DriverOps);
    if (driver == NX_NULL)
    {
        NX_LOG_E("create driver failed!");
        return;
    }

    /* call init */
    if (ProbeDriver(driver) != NX_EOK)
    {
        NX_LOG_E("pcnet32 init failed!");
        return;
    }

    if (NX_DriverRegister(driver) != NX_EOK)
    {
        NX_LOG_E("register driver %s failed!", DRV_NAME);
        NX_DriverCleanup(DRV_NAME);
        return;
    }

#if DEBUG_ARP == 1
    arp_test();
#endif
}

NX_PRIVATE void PCNET32_DriverExit(void)
{
    NX_DriverCleanup(DRV_NAME);
}

NX_DRV_INIT(PCNET32_DriverInit);
NX_DRV_EXIT(PCNET32_DriverExit);
